#pragma once

class Vec3f
{
public:
	Vec3f();
	Vec3f(const Vec3f &rhs);
	Vec3f(Vec3f &&rvalue);
	Vec3f(float x, float y, float z);
	~Vec3f();

	float x() const { return x_; }
	float &x() { return x_; }

	float y() const { return y_; }
	float &y() { return y_; }

	float z() const { return z_; }
	float &z() { return z_; }

	float length() const;

	float distance(const Vec3f &rhs) const;
	float distance(float x, float y, float z) const;

	Vec3f cross(const Vec3f &rhs) const;
	Vec3f cross(float x, float y, float z) const;

	float dot(const Vec3f &rhs) const;
	float dot(float x, float y, float z) const;

	Vec3f normalize();
	Vec3f invert();
	Vec3f inverted() const;

	Vec3f reflect(const Vec3f &rhs) const;
	Vec3f refract(const Vec3f &rhs, float eta) const;

	Vec3f clamp(float a, float b);
	static Vec3f clamp(const Vec3f &vec, float a, float b);
	static float clampf(float x, float a, float b);

	bool operator==(const Vec3f &rhs) const { return (x_ == rhs.x() && y_ == rhs.y()  && z_ == rhs.z()); }

	Vec3f operator+(const Vec3f &rhs) const { return Vec3f(x_+rhs.x(), y_+rhs.y(), z_+rhs.z()); }
	Vec3f operator+(float rhs) const { return Vec3f(x_+rhs, y_+rhs, z_+rhs); }

	Vec3f operator-(const Vec3f &rhs) const { return Vec3f(x_-rhs.x(), y_-rhs.y(), z_-rhs.z()); }
	Vec3f operator-(float rhs) const { return Vec3f(x_-rhs, y_-rhs, z_-rhs); }

	Vec3f operator/(const Vec3f &rhs) const { return Vec3f(x_/rhs.x(), y_/rhs.y(), z_/rhs.z()); }
	Vec3f operator/(float rhs) const { return Vec3f(x_/rhs, y_/rhs, z_/rhs); }

	Vec3f operator*(const Vec3f &rhs) const { return Vec3f(x_*rhs.x(), y_*rhs.y(), z_*rhs.z()); }
	Vec3f operator*(float rhs) const { return Vec3f(x_*rhs, y_*rhs, z_*rhs); }

	Vec3f operator/=(const Vec3f &rhs) { x_ /= rhs.x(); y_ /= rhs.y(); z_ /= rhs.z(); return *this; }
	Vec3f operator/=(float rhs) { x_ /= rhs; y_ /= rhs; z_ /= rhs; return *this; }

	Vec3f operator*=(const Vec3f &rhs) { x_ *= rhs.x(); y_ *= rhs.y(); z_ *= rhs.z(); return *this; }
	Vec3f operator*=(float rhs) { x_ *= rhs; y_ *= rhs; z_ *= rhs; return *this; }
	
private:
	float x_,y_,z_;
};
